"""
做一个可以给别人用的可执行程序，桌面软件
小说下载器：爬虫、正则解析、文件存储，GUI，exe打包
拓展：xpath解析，excel，数据库存储

项目：总文件夹-- xunter_novel
    app.py 主应用程序 界面交互程序
    bqg_reptile 笔趣阁的爬虫程序  (功能类)
"""
import threading

# 交互界面

# 安装环境  pip install ttkbootstrap

import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from ttkbootstrap.scrolled import ScrolledFrame
from bqg_reptile import BQGClient


class MainApp(ttk.Window): #继承ttk.Window类拥有创建窗口的功能
    def __init__(self):
        """
        主界面的初始化
        """
        # 调用父类ttk.window进行窗口的初始化，设置主题，设置标题，设置窗口大小
        super().__init__(themename='vapor',
                         title='Xunter小说下载器V1.0.0',
                         size=(400,600)
                         )
        self.iconbitmap('logo.ico')  #调用ttk.Window类中的图标方法
        self.wm_iconbitmap('logo.ico')  #调用ttk.Window类中的任务栏图标方法

        #因为功能操作都是在主窗口完成的所以可以直接在主窗口导入爬虫功能  在其他class类中如果要用到爬虫功能只要得到MainApp类就能用爬虫功能
        #导入爬虫功能  创建爬虫属性等于BQGClient实例对象
        self.reptile = BQGClient()

        #在主窗口中创建一个头部对象  也就是将头部div放到主窗口中
        HeaderDive(self).pack(side=TOP,pady=10) #顶部+y轴方向的外边距为10px

        #主窗口的HeaderDive下面是main_div
        #需要有对应的功能所以将main_div保存到属性里面
        self.main_div = MainDiv(self)
        self.main_div.pack(side=TOP,pady=10,fill=X)


class HeaderDive(ttk.Frame):
    def __init__(self, parent:MainApp):
        """
        头部框架
        :param parent:默认为主界面对象
        """
        super().__init__(parent)    #调用父类的初始化框架方法在parent主界面中创建一个框架  (创建一个div放在body里面)

        self.search_thread = None   #增加一个搜索线程初始为空  在搜索时就创建  优化搜索时程序卡住问题
        self.dowmload_work = {}     #因为下载时小说章节不止一章并且下载要传的参数也不止一个所以我们用一个字典来保存下载任务

        self.parent = parent

        ttk.Label(self,text='小说名称').pack(side=LEFT) # 在头部的div里面放一个p标签样式为左对齐

        self.novel_input = ttk.Entry(self)  #单行输入框
        self.novel_input.insert(0,'万相之王')
        self.novel_input.pack(side=LEFT)

        self.search_button = ttk.Button(self,text='搜索',command=self.search)  #command设置点击事件调用search方法
        self.search_button.pack(side=LEFT,padx=10)

    def search(self):
        """
        搜索按钮点击事件
        :return:搜索到的小说名称
        """
        #获取输入框的内容
        novel_name = self.novel_input.get()
        # print(novel_name)

        # #调用笔趣阁爬虫API类中的搜索小说方法
        # #通过self.parent得到MainApp类再调用其中的reptile方法得到BQGClient对象
        # #再调用对象中的search_book方法传入需要的数据进行小说查找
        # reptile = self.parent.reptile
        # res = reptile.search_book(novel_name)      #这里会发起网络请求 会耗时并且在程序中会卡住  所以我们要新开一个线程  进行异步操作
        # print(res)

        #线程改进
        if self.search_thread is None or not self.search_thread.is_alive(): #如果搜索线程为空或者没有进行搜索
            self.search_button.configure(state='disabled')      #在搜索的过程中将搜索按钮禁用     在搜索结束后接触禁用
            self.search_thread = threading.Thread(target=self.search_work,args=(novel_name,))   #就调用线程模块中的线程方法创建一个线程  给线程分配工作任务 并且通过args传入参数 注意要加逗号 因为传的是元组
            self.search_thread.start()  #开始线程任务

    #定义一个搜索的工作任务
    def search_work(self,novel_name:str):
        """
        搜索任务
        :param novel_name:小说名称
        :return:
        """
        reptile = self.parent.reptile  #爬虫对象
        main_div = self.parent.main_div  #main_div对象

        #搜索之前 清空之前添加的搜索结果
        [i.destroy() for i in main_div.novel_lis]

        res = reptile.search_book(novel_name)   #搜索出的结果
        # print(res)
        self.search_button.configure(state='normal')  #接触禁止状态

        if res == 1:
            ttk.Label(main_div.sf,text='暂无该小说').pack(side=TOP,pady=10)
        else:
            for i in res:
                li_div = ttk.Frame(main_div.sf)    #创建li_div在main_div的sf滚动盒子里面

                text = '%s_%s' % (i['articlename'],i['author'])
                ttk.Label(li_div,text=text,font=20).pack(side=LEFT) #在li_div里面创建label

                ttk.Button(li_div,
                           text='下载',
                           command=lambda url=i['url_list'],title=text:self.dowmload(url, title)
                           ).pack(side=RIGHT)

                li_div.pack(side=TOP,pady=10,padx=10,fill=X)

                main_div.novel_lis.append(li_div)   #将li_div保存到小说列表中去
    def dowmload(self,url:str, title:str):
        """
        下载事件
        :param url: 小说资源地址
        :param title: 小说标题
        :return:
        """
        # print(url,title)
        # 在主界面新开一个窗口 展示下载进度
        new_window = ttk.Toplevel(master=self.parent,title=f'{title}下载中',size=(400,100))

        #在新窗口中创建一个p标签  提示信息文本
        info_label = ttk.Label(new_window,text='请求中。。。')
        info_label.pack(side=TOP,pady=10,padx=10,fill=X)

        #进度条  下载进程
        dowmload_progress = ttk.Progressbar(new_window,bootstyle='striped')
        dowmload_progress.pack(side=TOP,pady=10,padx=10,fill=X)

        self.dowmload_work[url]={
            'new_window':new_window,
            'info_label':info_label,
            'dowmload_progress':dowmload_progress
        }

        #开一个下载线程  专门进行下载操作  避免卡死
        dowm_thread = threading.Thread(target=self.download_work,args=(url,title))
        dowm_thread.start()

    def download_work(self,url:str,title:str):
        """
        下载工作线程
        :param url:
        :param title:
        :return:
        """
        reptile = self.parent.reptile  # 爬虫对象
        pages = reptile.get_pages(url)[:20]
        # print(pages)

        #提示信息修改
        #得到当前下载任务状态
        now_work = self.dowmload_work[url]

        count = 0   #下载进度
        now_work['info_label']['text'] = f'{count}/{len(pages)}'    #修改提示信息
        now_work['dowmload_progress']['maximum'] = len(pages)       #设置进度条的最大长度为页数的长度

        #文件的写入
        with open(r'./%s/%s.txt' % (reptile.download_path,title),'w',encoding='utf-8') as f:
            for page_url,page_title in pages:
                f.write(page_title+'\n')

                page_txt = reptile.download_page(page_url)
                if page_txt :
                    f.write(page_txt+'\n')
                    f.flush()

                #更新下载信息
                count +=1
                now_work['info_label']['text'] = f'{count}/{len(pages)}'
                now_work['dowmload_progress']['value'] = count
            else:
                now_work['info_label']['text'] = f'下载完成：{len(pages)}'








class MainDiv(ttk.LabelFrame):
    def __init__(self, parent:MainApp):
        super().__init__(parent,text='搜索结果',bootstyle="info")

        self.novel_lis = []     #小说列表

        self.sf = ScrolledFrame(self,autohide=True, height=500)     #设置滚动条 放在MainDiv里面  自动滚动
        self.sf.pack(fill=BOTH, expand=YES)        #滚动条填充  宽高百分百



if __name__ == '__main__':
    app = MainApp()
    app.mainloop()


